package bingo.codeweaver.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import net.bingosoft.common.config.Config;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import bingo.codeweaver.core.factory.DatabaseFactory;
import bingo.codeweaver.core.metadata.ClassMetadata;
import bingo.codeweaver.core.metadata.DatabaseMetadata;
import bingo.codeweaver.core.metadata.TableMetadata;
import bingo.codeweaver.core.strategy.DefaultNamingStrategy;
import bingo.codeweaver.core.strategy.NamingStrategy;
import bingo.codeweaver.core.template.TemplateEngine;
import bingo.codeweaver.core.template.TemplateLibrary;
import bingo.codeweaver.core.template.TemplateMetadata;
import bingo.codeweaver.core.template.VelocityTemplateEngine;
import bingo.codeweaver.core.utils.FileHelper;


/**
 * Mar 9, 2009 3:17:21 AM CodeGenerator.java
 * Author:yangdongyu(yangdy@bingosoft.net)
 * ReadMe:
 * ChangeLog:
 */
public class CodeRobot {
	private static NamingStrategy namingStrategy = new DefaultNamingStrategy();
	private static TemplateEngine engine = new VelocityTemplateEngine();
	private static DatabaseFactory factory = new DatabaseFactory(true);
	private static String currentDatasourceName;
	
	public static void setCurrentDatasourceName(String currentDatasourceName) {
		CodeRobot.currentDatasourceName = currentDatasourceName;
	}

	public static DatabaseFactory getFactory() {
		return factory;
	}

	public static void setFactory(DatabaseFactory factory) {
		CodeRobot.factory = factory;
	}

	public static NamingStrategy getNamingStrategy() {
		return namingStrategy;
	}

	public static void setNamingStrategy(NamingStrategy namingStrategy) {
		CodeRobot.namingStrategy = namingStrategy;
	}

	public static TemplateEngine getEngine() {
		return engine;
	}

	public static void setEngine(TemplateEngine engine) {
		CodeRobot.engine = engine;
	}
	
	/**
	 * 获取当前的数据库
	 * @param tablespace 表空间的名称
	 * @return
	 * @throws SQLException
	 */
	public static DatabaseMetadata currentDatabase(String tablespace) throws Exception{
		BeanFactory bf = new ClassPathXmlApplicationContext(new String[]{"classpath:/spring/dataAccessContext.xml","classpath:/spring/applicationContext.xml"});
		String dataSourceName = currentDatasourceName == null ? "dataSource" : currentDatasourceName;
		DataSource ds = (DataSource)bf.getBean(dataSourceName);
		return factory.create(ds, tablespace);
	}
	

	/**
	 * 创建给定数据源对应的数据库
	 * @param ds
	 * @param tableSpace 表空间的名称
	 * @return
	 * @throws SQLException
	 */
	public static DatabaseMetadata createDatabase(DataSource ds,String tableSpace) throws Exception{
		return factory.create(ds, tableSpace);
	}
	

	
	/**
	 * 根据指定的模板和用户指定的上下文数据生成代码
	 * @param template
	 * @param userContext
	 * @return
	 * @throws Exception
	 */
	public static String generateCode(String template,Map<String,Object> userContext) throws Exception{
		String content = engine.evaluate(template, TemplateLibrary.getTemplate(template), userContext);
		return content;
	}
	/**
	 * 根据指定的模板和表的名称和用户指定的上下文数据生成代码
	 * @param template
	 * @param tableName
	 * @param userContext
	 * @return
	 * @throws Exception
	 */
	public static String generateCode(String tablespace,String tableName,String template,Map<String,Object> userContext,TableMetadata table) throws Exception{
		
		Map<String,Object> context = new HashMap<String, Object>();
		context.put("table", table);
		context.put("className", table.getClassName());
		context.put("comment", table.getComments());
		if(userContext != null && !userContext.isEmpty()){
			context.putAll(userContext);
		}
		String content = engine.evaluate(template, TemplateLibrary.getTemplate(template), context);
		return content;
	}
	
	
	/**
	 * 根据表名生成业务模型对象
	 * @param tablespace 表空间名称
	 * @param tableName 表名称
	 * @param pkgName 业务模型所在表名称
	 * @param targetFolder 类输出路径
	 * @return 
	 * @throws Exception
	 */
	public static String generateModelByTable(String tablespace,String tableName,String pkgName,String targetFolder) throws Exception{
		
		
		Map<String,Object> context = new HashMap<String, Object>();
		
		TableMetadata table = currentDatabase(tablespace).getTable(tableName.toUpperCase());
		context.put("comment", table.getComments() );
		context.put("pkgName", pkgName);
		String model = generateCode(tablespace, tableName,"BussinessObject", context,table);
		String outputDir = (targetFolder.endsWith("/") ? targetFolder : (targetFolder+"/"))  + pkgName.replaceAll("\\.", "/") ;
		if(!outputDir.endsWith("/")){
			outputDir +="/";
		}
		outputDir += namingStrategy.toClassName(tableName) + ".java";
		
		FileHelper.createFile(outputDir, model, "UTF-8");
		return model;
	}
	/**
	 * 根据表名生成业务模型对象
	 * @param tablespace 表空间名称
	 * @param csvTableName 用逗号分隔表名称
	 * @param pkgName 业务模型所在表名称
	 * @param targetFolder 类输出路径
	 * @return
	 * @throws Exception
	 */
	public static void generateModelByTableNames(String tablespace,String csvTableName,String pkgName,String targetFolder) throws Exception{
		Map<String,Object> context = new HashMap<String, Object>();
		context.put("pkgName", pkgName);
		
		DatabaseMetadata db = currentDatabase(tablespace);
		String [] tableNames = csvTableName.split(",");
		//输出模型对应的目录
		String outputDir = (targetFolder.endsWith("/") ? targetFolder : (targetFolder+"/"))  + pkgName.replaceAll("\\.", "/") ;
		if(!outputDir.endsWith("/")){
			outputDir +="/";
		}
		
		for(String tableName : tableNames){
			TableMetadata table  = db.getTable(tableName.toUpperCase());
			context.put("comment", table.getComments());
			String model = generateCode(tablespace, tableName,"BussinessObject", context,table);
			FileHelper.createFile(outputDir + namingStrategy.toClassName(tableName) + ".java", model, "UTF-8");
		}
	}

	/**
	 * 根据指定的模板和上下文数据为指定的表生成代码
	 * @param template 模板
	 * @param tableName 表名称
	 * @param userContext 用户上下文
	 * @return
	 * @throws Exception
	 */
	public static String generateCode(TemplateMetadata template,String tablespace,String tableName,Map<String,Object> userContext) throws Exception{
		TableMetadata table = currentDatabase(tablespace).getTable(tableName);
		if(table == null){
			throw new IllegalArgumentException("No table with name ["+tableName+"] is found in tablesapce ["+tablespace+"]");
		}
		Map<String,Object> context = new HashMap<String, Object>();
		context.put("table", table);
		context.put("className", table.getClassName());
		context.put("comment", table.getComments());
		if(userContext != null && !userContext.isEmpty()){
			context.putAll(userContext);
		}
		return engine.merge(template, context);
	}
	
	public static void generateModule(String srcProject,String targetProject,String moduleName,String tablespace) throws Exception{
		DatabaseMetadata database = currentDatabase(tablespace);
		TableMetadata table = database.getTable(namingStrategy.toTableName(moduleName));
		Map<String,Object> context = new HashMap<String, Object>();
		context.put("context", table);
		Properties props = Config.getProperties();
		for(Object key : props.keySet()){
			context.put(key.toString(), props.get(key));
		}
		copyFile(new File(srcProject), new File(targetProject), true, moduleName, context);
	}
	
	/**
	 * 在指定模块生成对应的包和相应的类
	 * @param targetFolder
	 * @param pkg
	 * @param moduleName
	 * @throws Exception
	 */
	public static void generateModule(String targetFolder,String pkg,String moduleName,String tablespace,String tableName) throws Exception{
		String rootPath = (targetFolder.endsWith("/") ? targetFolder : (targetFolder+"/"))  + pkg.replaceAll("\\.", "/") ;
		if(!rootPath.endsWith("/")){
			rootPath +="/";
		}
		String lowerCaseModuleName = moduleName.toLowerCase();
		
		String firtCharUpperModuleName = Character.toUpperCase(moduleName.charAt(0)) + moduleName.substring(1);
		//FileHelper.deleteFile(rootPath + lowerCaseModuleName);
		String modelClassName = namingStrategy.toClassName(tableName);
		String[] moudleCls = {
				 rootPath + lowerCaseModuleName+ "/web/" + firtCharUpperModuleName + "Action.java"
				,rootPath + lowerCaseModuleName+ "/model/" + modelClassName + ".java"
				,rootPath + lowerCaseModuleName+ "/service/" + firtCharUpperModuleName + "Service.java"
				};

		Map<String,Object> context = new HashMap<String,Object>(); 
		context.put("moduleName", firtCharUpperModuleName);
		context.put("modelClassName", modelClassName);
		context.put("servicePropertyName", Character.toLowerCase(moduleName.charAt(0)) + moduleName.substring(1) + "Service");
		context.put("actionPkgName", pkg + "." + lowerCaseModuleName + ".web");
		context.put("servicePkgName", pkg + "." + lowerCaseModuleName + ".service");
		context.put("modelPkgName", pkg + "." + lowerCaseModuleName + ".model");
		
		TableMetadata table = currentDatabase(tablespace).getTable(tableName.toUpperCase());
		
		
		ClassMetadata modelClassMetadata = new ClassMetadata(table);
		
		context.put("modelClass", modelClassMetadata);
		
		//create action
		if(new File(moudleCls[0]).exists()){
			context.put("actionManualCode", readManualCode(moudleCls[0]));
		}
		String action = generateCode("Action",context);
		
		FileHelper.createFile(moudleCls[0], action, "UTF-8");
		
		//create model
		if(new File(moudleCls[1]).exists()){
			context.put("modelManualCode", readManualCode(moudleCls[1]));
		}
		context.put("pkgName", context.get("modelPkgName").toString());
		String model = generateCode(tablespace, tableName,"BussinessObject", context,table);
		FileHelper.createFile(moudleCls[1], model, "UTF-8");
		
		//create service
		
		if(new File(moudleCls[2]).exists()){
			context.put("serviceManualCode", readManualCode(moudleCls[1]));
		}
		String service = generateCode("Service",context);
		FileHelper.createFile(moudleCls[2], service, "UTF-8");
		
		
		
	}
	/**
	 * 读取手工编写的代码
	 * @param filePath
	 * @return
	 * @throws FileNotFoundException
	 */
	private static String readManualCode(String filePath) throws FileNotFoundException {
		String content = FileHelper.readFileContent(filePath,"UTF-8");
		String result = null;
		int start = 0,end = 0;
		if((start = content.indexOf("<manual-code>")) != -1 
				&& (end = content.indexOf("</manual-code>")) != -1 
				&& end > start){
			result = content.substring(start+13, end-2).trim();
		}
			
		return result;
	}
	
	/**
	 * 为实体生成表定义语句
	 * @param entity
	 * @return
	 * @throws Exception 
	 */
	public static String generateDDLForEntity(Class entity) throws Exception{
		TableMetadata table = new TableMetadata(entity);
		Map<String,Object> context = new HashMap<String, Object>();
		context.put("table", table);
		return generateCode("generateDDLForEntity", context);
		
	}
	
	protected static void  copyFile(File srcFile,File targetFile,boolean isRoot,String moduleName,Map<String,Object> context) throws Exception{
		if(srcFile.isDirectory()){//目录
			if (srcFile.getName().equalsIgnoreCase(".svn")
					|| srcFile.getName().endsWith("ignore")){
				return;
			}
			File[] childFiles = srcFile.listFiles();
			
			File newTarget = new File(targetFile.getPath()+ (isRoot ? "" : (File.separator + srcFile.getName())));
			if (!newTarget.exists())
				newTarget.mkdirs();
			
			for (File childFile : childFiles) {//文件
				copyFile(childFile,newTarget, false, moduleName,context);
			}
		}else{
			String srcFileName = srcFile.getName();
			String tmpName = moduleName;
			if(srcFileName.endsWith(".java")){
				tmpName = namingStrategy.toClassName(moduleName);
			}else if(srcFileName.endsWith(".jsp")){
				tmpName = namingStrategy.toColumnName(moduleName);
			}
			String targetFileName = (targetFile.getAbsolutePath() + File.separator + moduleName.toLowerCase() + File.separator + srcFileName).replace("\\$moduleName", tmpName);
			if(srcFileName.endsWith(".vm")){
				targetFileName = targetFileName.substring(0, targetFileName.lastIndexOf(".vm"));
				TemplateMetadata template = new TemplateMetadata(srcFileName,"velocity",srcFile.getAbsolutePath());
				FileHelper.createFile(targetFileName, engine.merge(template, context), "UTF-8");
			}else{
				FileHelper.createFile(targetFileName, FileHelper.readFileContent(srcFile.getAbsolutePath()), "UTF-8");
			}
		}
	}
	
	
	public static void main(String[] args) throws Exception {
		generateModelByTable("USERS", "Fun_Business_Domain", "bingo.basp.bizdomain.model", "D:\\workspace\\bingo.kissWeb\\src\\main\\bingo\\codeweaver\\core\\");
	}
	

}
